using System;
using System.IO;

using Microsoft.Practices.EnterpriseLibrary.Logging.Formatters;
using Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners;

namespace SiteParser.Core.Logging
{
    /// <summary>
    /// Performs logging to a file and rolls the output file when either time or size thresholds are 
    /// exceeded. History log files are removed based on purge interval parameter.
    /// </summary>
    public class PurgingRollingFlatFileTraceListener : RollingFlatFileTraceListener
    {
        private readonly PurgeHelper internalPurgeHelper;
        private readonly string fileName;
        private readonly int purgeIntervalDays;

        public PurgingRollingFlatFileTraceListener(
            string fileName,
            string header,
            string footer,
            ILogFormatter formatter,
            int rollSizeKB,
            string timeStampPattern,
            RollFileExistsBehavior rollFileExistsBehavior,
            RollInterval rollInterval,
            int purgeIntervalDays)
            : base(fileName, header, footer, formatter, rollSizeKB, timeStampPattern, rollFileExistsBehavior, rollInterval)
        {
            this.fileName = RootFileName(fileName);
            this.purgeIntervalDays = purgeIntervalDays;
            internalPurgeHelper = new PurgeHelper(this);
        }

        public override void TraceData(System.Diagnostics.TraceEventCache eventCache, string source, System.Diagnostics.TraceEventType eventType, int id, object data)
        {
            internalPurgeHelper.PurgeIfNeeded();
            base.TraceData(eventCache, source, eventType, id, data);
        }

        private static string RootFileName(string fileName)
        {
            string rootedFileName = fileName;
            if (!Path.IsPathRooted(rootedFileName))
            {
                rootedFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, rootedFileName);
            }
            return rootedFileName;
        }


        internal sealed class PurgeHelper
        {
            private DateTime lastPurgeTime;
            private readonly object sync = new object();
            private readonly PurgingRollingFlatFileTraceListener owner;

            public PurgeHelper(PurgingRollingFlatFileTraceListener owner)
            {
                this.owner = owner;
                lastPurgeTime = DateTime.MinValue;
            }

            public void PurgeIfNeeded()
            {
                if (owner.purgeIntervalDays > 0)
                {
                    if (lastPurgeTime.AddHours(1) < DateTime.Now)
                    {
                        lock (sync)
                        {
                            if (lastPurgeTime.AddHours(1) < DateTime.Now)
                            {
                                lastPurgeTime = DateTime.Now;
                                Purge();
                            }
                        }
                    }
                }
            }

            private void Purge()
            {
                string logPath = Path.GetDirectoryName(owner.fileName);
                if (!Directory.Exists(logPath))
                {
                    // nothing to purge
                    return;
                }
                string logFileNameWithoutExtension = Path.GetFileNameWithoutExtension(owner.fileName);
                string logFileNameExtenstion = Path.GetExtension(owner.fileName);
                string searchFilePattern = String.Concat(logFileNameWithoutExtension, "*", logFileNameExtenstion);
                
                string[] logFiles = Directory.GetFiles(logPath, searchFilePattern);
                if (logFiles.Length > 0)
                {
                    DateTime purgeDateTime = DateTime.Now.AddDays(-owner.purgeIntervalDays);
                    foreach (string logFileName in logFiles)
                    {
                        if (File.GetLastWriteTime(logFileName) <= purgeDateTime)
                        {
                            try
                            {
                                File.Delete(logFileName);
                            }
                            catch { } // swallow all errors
                        }
                    }
                }
            }
        }
    }
}
